# --*-- conding:utf-8 --*--
# @Time : 2025/1/29 19:58
# @Author : Mark
# @Email : 17521559996@163.com
# @File : purchase_order.py
# @Software : PyCharm
from odoo import fields, models, api
from odoo.exceptions import ValidationError, UserError
import logging
from markupsafe import Markup
import base64
from io import BytesIO
from openpyxl import load_workbook
_logger = logging.getLogger(__name__)


class purchase_order(models.Model):
    _inherit = 'purchase.order'

    customer_reference = fields.Char(string='客户参考号')
    product_number = fields.Text(string='产品编号',)
    file = fields.Binary(string='文件')
    file_name = fields.Char(string='文件名')
    sale_order_id = fields.Many2one('sale.order', string='来源销售订单', readonly=True)


    def button_exchange(self):
        self.ensure_one()
        return {
            'type': 'ir.actions.act_window',
            'name': '税项选择',
            'view_mode': 'form',
            'res_model': 'tax.selection',
            'target': 'new',
            'context': {
                'default_purchase_id': self.id,
            },
        }
    def button_confirm(self):
        res = super().button_confirm()
        for order in self:
            for picking in order.picking_ids:
                picking.customer_reference = order.customer_reference
        return res
    @api.with_redis_client
    def action_create_product(self, redis_client=None):
        """
        生成产品：主方法，负责协调整个流程，使用消息记录而非抛出异常
        """
        for item in self:
            if not item.file:
                item.message_post(body=Markup("<p class='alert alert-warning'>请先上传Excel文件</p>"))
                return

            product_numer_list = item.product_number.split('\n')

            # 验证并获取必要的Excel数据
            try:
                excel_id = self._validate_product_excel()
                price_ids = self._validate_price_excel()
            except UserError as e:
                item.message_post(body=Markup(f"<p class='alert alert-danger'>{str(e)}</p>"))
                return

            if not redis_client:
                item.message_post(body=Markup("<p class='alert alert-danger'>Redis连接失败</p>"))
                return

            # 统计结果数据
            stats = {
                'total': len(product_numer_list),
                'success': 0,
                'not_found': 0,
                'price_missing': 0,
                'processed': [],
                'failed': []
            }

            # 只读取需要的产品数据
            data_hash = self._get_product_base_data(redis_client, excel_id, product_numer_list)

            # 只读取需要的价格表数据
            price_data_all = self._get_price_data(redis_client, price_ids, product_numer_list)

            # 处理每个产品，安全模式
            self._process_products_safe(item, product_numer_list, data_hash, price_data_all, stats)

            # 发布处理结果摘要
            self._post_import_summary(item, stats)

    @api.with_redis_client
    def cron_create_purchase(self, data, redis_client=None):
        """
        定时任务执行导入询价单
        """
        # 先创建采购订单
        purchase_order = self.env['purchase.order'].create({
            'partner_id': self.env.user.partner_id.id,
            'date_order': fields.Datetime.now(),
            'state': 'draft',
        })

        #
        try:
            excel_id = purchase_order._validate_product_excel()
            price_ids = purchase_order._validate_price_excel()
        except UserError as e:
            purchase_order.message_post(body=Markup(f"<p class='alert alert-danger'>{str(e)}</p>"))
            return

        if not redis_client:
            purchase_order.message_post(body=Markup("<p class='alert alert-danger'>Redis连接失败</p>"))
            return

        # 统计结果数据
        stats = {
            'total': len(data),
            'success': 0,
            'not_found': 0,
            'price_missing': 0,
            'processed': [],
            'failed': []
        }

        # 只读取需要的产品数据
        data_hash = purchase_order._get_product_base_data(redis_client, excel_id, data)

        # 只读取需要的价格表数据
        price_data_all = purchase_order._get_price_data(redis_client, price_ids, data)

        # 处理每个产品，安全模式
        purchase_order._process_products_safe(purchase_order, data, data_hash, price_data_all, stats)

        # 发布处理结果摘要
        purchase_order._post_import_summary(purchase_order, stats)

        return True

    def _validate_product_excel(self):
        """验证并获取产品基础信息Excel"""
        excel_id = self.env['spreadsheet.excel'].search([('category', '=', 'product')], limit=1)
        if not excel_id:
            raise UserError('请先上传产品表')
        return excel_id

    def _validate_price_excel(self):
        """验证并获取价格表Excel"""
        price_ids = self.env['spreadsheet.excel'].search([('category', '=', 'price')])
        if not price_ids:
            raise UserError('请先上传价格表')
        return price_ids

    def _get_product_base_data(self, redis_client, excel_id, product_codes):
        """从Redis获取指定产品的基础数据

        Args:
            redis_client: Redis客户端
            excel_id: Excel记录
            product_codes: 需要获取的产品编码列表
        """
        import json

        redis_main_key = str(excel_id.sequence)
        data_hash = {}

        # 使用pipeline批量处理Redis命令，提高性能
        pipeline = redis_client.redis_conn.pipeline()

        # 方案1: 使用hmget一次获取多个产品数据(适用于较少产品)
        if len(product_codes) <= 100:  # 适当的阈值，避免命令过长
            # 获取多个字段的值
            values = redis_client.redis_conn.hmget(redis_main_key, product_codes)
            for i, v in enumerate(values):
                if v:
                    k = product_codes[i]
                    if isinstance(v, bytes):
                        v = v.decode('utf-8')
                    data_hash[k] = v

        # 方案2: 针对较多产品，使用pipeline批量处理多个hget命令
        else:
            for prod_code in product_codes:
                pipeline.hget(redis_main_key, prod_code)

            # 执行所有命令并获取结果
            results = pipeline.execute()

            for i, v in enumerate(results):
                if v:
                    k = product_codes[i]
                    if isinstance(v, bytes):
                        v = v.decode('utf-8')
                    data_hash[k] = v

        _logger.info(f"已获取{len(data_hash)}个产品的基础数据")
        return data_hash

    def _get_price_data(self, redis_client, price_ids, product_codes):
        """从Redis获取指定产品的价格表数据

        Args:
            redis_client: Redis客户端
            price_ids: 价格表记录集
            product_codes: 需要获取价格的产品代码列表
        """
        import json

        price_data_all = {}
        for product_code in product_codes:
            price_data_all[product_code] = []

        # 对每个价格表使用pipeline批量处理
        for price_excel in price_ids:
            price_key = str(price_excel.sequence)

            # 创建pipeline
            pipeline = redis_client.redis_conn.pipeline()

            # 批量添加所有产品的hget命令
            for product_code in product_codes:
                pipeline.hget(price_key, product_code)

            # 执行所有命令并获取结果
            results = pipeline.execute()

            # 处理结果
            for i, raw_price in enumerate(results):
                product_code = product_codes[i]

                if not raw_price:
                    continue

                # 解码和解析单个价格记录
                if isinstance(raw_price, bytes):
                    raw_price = raw_price.decode('utf-8')

                try:
                    price_record = json.loads(raw_price)
                    price_data_all[product_code].append(price_record)
                except (json.JSONDecodeError, TypeError) as e:
                    _logger.warning(f"解析产品 {product_code} 的价格数据失败: {e}")

        _logger.info(f"已获取{len(product_codes)}个产品的价格数据")
        return price_data_all

    def _process_products_safe(self, item, product_numer_list, data_hash, price_data_all, stats):
        """安全处理产品列表，创建产品和相关记录，使用message_post而非抛出异常"""
        import json

        for prod_key in product_numer_list:
            # 使用 prod_key 查找对应记录的 JSON 数据
            record_json = data_hash.get(prod_key)
            if not record_json:
                # 记录未找到的产品
                msg = Markup(f"<p class='alert alert-warning'>产品编号 <b>{prod_key}</b> 在产品表中未找到对应记录</p>")
                item.message_post(body=msg)
                stats['not_found'] += 1
                stats['failed'].append(prod_key)
                continue

            try:
                record = json.loads(record_json)
                product_code = record.get('产品编码')

                # 创建或更新产品
                product_id = self._create_or_update_product(record)

                # 获取该产品的所有供应商价格信息和最优供应商
                supplier_price_list, best_supplier_info = self._get_all_supplier_prices(product_code, price_data_all)

                if supplier_price_list:
                    # 为产品添加所有供应商的价格规则
                    self._handle_multiple_suppliers(product_id, supplier_price_list)

                    # 获取最低价格用于采购订单行
                    best_price = 0.0
                    if best_supplier_info:
                        try:
                            best_price = float(best_supplier_info.get('供应商价格', 0))
                        except (ValueError, TypeError):
                            best_price = 0.0

                    # 创建采购明细并使用最低价格
                    self._create_purchase_line(product_id, best_price)

                    stats['success'] += 1
                    stats['processed'].append(prod_key)
                else:
                    # 没有价格信息但仍创建产品行
                    self._create_purchase_line(product_id)
                    msg = Markup(f"<p class='alert alert-warning'>产品 <b>{product_code}</b> 无供应商价格信息</p>")
                    item.message_post(body=msg)
                    stats['price_missing'] += 1
                    stats['processed'].append(prod_key)

                _logger.info(f"处理产品 {prod_key}: {record.get('描述') or ''}")

            except Exception as e:
                # 处理过程中出现错误
                error_msg = Markup(f"<p class='alert alert-danger'>处理产品 {prod_key} 时出错: {str(e)}</p>")
                item.message_post(body=error_msg)
                _logger.exception(f"处理产品 {prod_key} 时出错")
                stats['not_found'] += 1
                stats['failed'].append(prod_key)

    def _create_or_update_product(self, record):
        """创建或更新产品"""
        product_code = record.get('产品编码')
        product_id = self.env['product.product'].search([('default_code', '=', product_code)])

        # 创建品牌
        brand_id = self.env['gs.barnd'].search([('name', '=', record.get('品牌'))])
        if not brand_id:
            brand_id = self.env['gs.barnd'].create({
                'name': record.get('品牌'),
            })

        if not product_id:
            product_tmpl = self.env['product.template'].create({
                'name': record.get('描述') or product_code,
                'default_code': product_code,
                'new_variable': record.get('替换编码/新编码'),
                'description': record.get('描述'),
                'brand_id': brand_id.id,
                'origin': record.get('原产地'),
                'weight_gram': record.get('克重'),
                'length_cm': record.get('长'),
                'width_cm': record.get('宽'),
                'height_cm': record.get('高'),
                'is_storable': True,
            })
            return product_tmpl.product_variant_id
        else:
            return product_id[0]

    def _get_all_supplier_prices(self, product_code, price_data_all):
        """获取所有供应商价格信息，并找出最低价格的供应商

        Returns:
            tuple: (有效供应商列表, 最低价格供应商信息)
        """
        valid_suppliers = []
        best_supplier_info = None
        lowest_price = float('inf')

        # 确保类型匹配 - 将产品编码转为字符串
        str_product_code = str(product_code)

        # 确定价格信息列表
        price_info_list = []
        if product_code in price_data_all:
            price_info_list = price_data_all[product_code]
        elif str_product_code in price_data_all:
            price_info_list = price_data_all[str_product_code]
        else:
            # 尝试查找可能的其他格式
            potential_keys = [k for k in price_data_all.keys() if str(k).strip() == str_product_code]
            if potential_keys:
                price_info_list = price_data_all[potential_keys[0]]
            else:
                # 没有找到匹配项
                return [], None

        # 处理所有供应商信息
        for price_info in price_info_list:
            try:
                supplier_price = float(price_info.get('供应商价格', 0))
                if supplier_price <= 0:
                    continue

                # 添加到有效供应商列表
                valid_suppliers.append(price_info)

                # 更新最低价格供应商
                if supplier_price < lowest_price:
                    lowest_price = supplier_price
                    best_supplier_info = price_info

            except (ValueError, TypeError):
                # 价格转换失败，跳过此记录
                continue

        return valid_suppliers, best_supplier_info

    def _handle_multiple_suppliers(self, product_id, supplier_price_list):
        """处理多个供应商的价格和库存

        Args:
            product_id: 产品记录
            supplier_price_list: 供应商价格信息列表
        """
        for price_info in supplier_price_list:
            supplier_name = price_info.get('供应商')
            supply_type = price_info.get('采购/现货')

            if not supplier_name:
                continue

            # 转换数值
            try:
                qty = price_info.get('数量', 1)
                supplier_price = price_info.get('供应商价格', 0)
            except (ValueError, TypeError):
                qty = 1.0
                supplier_price = 0.0

            if supplier_price <= 0:
                continue

            # 创建或查找供应商
            partner_id = self._create_or_find_supplier(supplier_name)
            if not partner_id:
                continue

            # 创建供应商价格规则
            self._create_supplier_pricelist(product_id, partner_id, supplier_price)

            # 如果是现货，创建库存
            if supply_type and '现货' in supply_type:
                self._create_inventory_for_stock(product_id, qty, supplier_name)

    def _create_or_find_supplier(self, supplier_name):
        """创建或查找供应商"""
        if not supplier_name:
            return False

        partner_id = self.env['res.partner'].search([
            ('name', '=', supplier_name),
            ('supplier_rank', '>', 0)
        ], limit=1)

        if not partner_id:
            partner_id = self.env['res.partner'].create({
                'name': supplier_name,
                'supplier_rank': 1,  # 设置为供应商
                'company_type': 'company',
            })

        return partner_id

    def _create_supplier_pricelist(self, product_id, partner_id, price):
        """创建供应商价格规则"""
        if not partner_id or not product_id or price <= 0:
            return

        # 检查是否已存在此供应商的价格规则
        existing_rule = self.env['product.supplierinfo'].search([
            ('product_tmpl_id', '=', product_id.product_tmpl_id.id),
            ('partner_id', '=', partner_id.id),
        ], limit=1)

        if existing_rule:
            # 如果新价格更低，更新价格
            if price < existing_rule.price:
                existing_rule.price = price
        else:
            # 创建新的供应商价格规则
            self.env['product.supplierinfo'].create({
                'product_tmpl_id': product_id.product_tmpl_id.id,
                'partner_id': partner_id.id,
                'price': price,
                'min_qty': 1.0,
            })

    def _create_inventory_for_stock(self, product_id, quantity, supplier_name):
        """为现货产品创建或更新库存"""
        if quantity <= 0 or not supplier_name:
            return

        # 为每个供应商创建专属库位
        location_name = f"{supplier_name} 现货库存"
        stock_location = self.env['stock.location'].search([
            ('name', '=', location_name),
            ('usage', '=', 'internal')
        ], limit=1)

        if not stock_location:
            # 如果供应商专属库位不存在，创建新库位
            main_stock = self.env.ref('stock.stock_location_stock')
            stock_location = self.env['stock.location'].create({
                'name': location_name,
                'location_id': main_stock.id,
                'usage': 'internal',
                'comment': f'供应商 {supplier_name} 的现货库存位置'
            })

        # 查找该产品在此库位是否已有库存记录
        existing_quant = self.env['stock.quant'].search([
            ('product_id', '=', product_id.id),
            ('location_id', '=', stock_location.id)
        ], limit=1)

        if existing_quant:
            # 已存在库存记录，更新数量
            existing_quant.inventory_quantity = existing_quant.inventory_quantity + quantity
            existing_quant.action_apply_inventory()
        else:
            # 创建新的库存记录
            inventory_adj = self.env['stock.quant'].create({
                'product_id': product_id.id,
                'location_id': stock_location.id,
                'inventory_quantity': quantity,
            })
            inventory_adj.action_apply_inventory()

    def _create_purchase_line(self, product_id, supplier_price=0.0):
        """创建或更新采购订单明细行，避免重复创建"""
        # 查找产品的计量单位
        uom_id = product_id.uom_id.id or self.env.ref('uom.product_uom_unit').id

        # 使用供应商最低价格，如果没有则使用产品标准价
        final_price = supplier_price if supplier_price > 0 else (product_id.standard_price or 0.0)

        # 检查产品是否已存在于当前采购订单明细中
        existing_line = self.env['purchase.order.line'].search([
            ('order_id', '=', self.id),
            ('product_id', '=', product_id.id)
        ], limit=1)

        if existing_line:
            # 如果已存在，只更新价格（如果新价格更低）
            if final_price < existing_line.price_unit:
                existing_line.write({
                    'price_unit': final_price
                })
            # 如果需要也可以在这里更新数量
            # existing_line.product_qty += 1

            _logger.info(f"更新产品 {product_id.name} 的价格为 {final_price}")
        else:
            # 不存在则创建新的采购明细行
            self.env['purchase.order.line'].create({
                'product_id': product_id.id,
                'product_qty': 1,
                'product_uom': uom_id,
                'order_id': self.id,
                'name': product_id.name,
                'price_unit': final_price,
            })
            _logger.info(f"创建产品 {product_id.name} 的采购明细行，价格为 {final_price}")

    def _post_import_summary(self, item, stats):
        """生成并记录导入结果摘要"""
        summary = f"""
        <div class="alert alert-info">
            <h4>产品导入结果摘要</h4>
            <ul>
                <li>总计: <b>{stats['total']}</b> 个产品编号</li>
                <li>成功导入: <b>{stats['success']}</b> 个产品</li>
                <li>成功但无价格: <b>{stats['price_missing']}</b> 个产品</li>
                <li>未找到或出错: <b>{stats['not_found']}</b> 个产品</li>
            </ul>
        """

        # 如果有失败的产品，显示详情
        if stats['failed']:
            summary += "<p>未处理成功的产品编号:</p><ul>"
            for code in stats['failed'][:10]:  # 最多显示10个
                summary += f"<li>{code}</li>"

            if len(stats['failed']) > 10:
                summary += f"<li>...等共 {len(stats['failed'])} 个</li>"

            summary += "</ul>"

        summary += "</div>"
        # 使用Markup包装整个HTML
        item.message_post(body=Markup(summary))

    def action_clear_order_lines(self):
        #一键清空采购明细：批量等效垃圾桶，100%原生安全
        for order in self:
            order.order_line.unlink()
        return True
